package service

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"

	"cashway.com/cis/assistant/model"
	"cashway.com/cis/assistant/types"
	"cashway.com/cis/assistant/utils"
	"github.com/sirupsen/logrus"
)

func UploadFileToRemote(params *model.UploadFileToRemoteParams) error {
	logrus.Infof("开始上传文件至远程端")
	if params.Url == "" {
		return fmt.Errorf("url 不能为空")
	}

	file, err := os.Open(params.LocalFilePath)
	if err != nil {
		return err
	}

	client, err := utils.GetHttpClient(types.CreateHttpsClientParams{
		NotInsecureSkipVerify:           params.NotInsecureSkipVerify,
		PemFilePath:                     params.PemFilePath,
		IntermediateTerminalCertificate: params.IntermediateTerminalCertificate,
		CertFilePath:                    params.CertFilePath,
		KeyFilePath:                     params.KeyFilePath,
	})

	if err != nil {
		return err
	}

	fileName := filepath.Base(params.LocalFilePath)

	if params.Transfer == 1 {
		return breakpointResume(client, file, fileName, params.Url, params.RemoteDir, params.Block)
	}

	return fullUpload(client, file, fileName, params.Url, params.RemoteDir)
}

func fullUpload(client *http.Client, file *os.File, fileName string, url string, remoteDir string) error {
	logrus.Infof("开始整体上传文件至远程端，filename=%s", fileName)
	var body bytes.Buffer
	multipartWriter := multipart.NewWriter(&body)

	formFileWriter, err := multipartWriter.CreateFormFile("file", fileName)

	if err != nil {
		return err
	}

	_, copyErr := io.Copy(formFileWriter, file)

	if copyErr != nil {
		return copyErr
	}

	multipartWriter.WriteField("filePath", remoteDir)
	multipartWriter.WriteField("transfer", "0")

	bodyCloseErr := multipartWriter.Close()
	if bodyCloseErr != nil {
		return bodyCloseErr
	}

	resp, err := client.Post(url, multipartWriter.FormDataContentType(), &body)

	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("远程端响应码为：%d", resp.StatusCode)
	}
	logrus.Info("整体上传文件至远程端成功")
	return nil
}

func breakpointResume(client *http.Client, file *os.File, fileName string, url string, remoteDir string, block int) error {
	logrus.Infof("断点续传上传文件至远程端，filename=%s", fileName)
	var pos int
	if block <= 0 {
		block = 1024
	}
	var md5Res string
	buf := make([]byte, block)
	for {
		size, err := file.Read(buf)

		if err != nil && err != io.EOF {
			return err
		}

		if size == 0 {
			break
		}

		var body bytes.Buffer
		multipartWriter := multipart.NewWriter(&body)

		formFileWriter, err := multipartWriter.CreateFormFile("file", fileName)

		if err != nil {
			return err
		}

		writeSize, err := formFileWriter.Write(buf[:size])

		if err != nil {
			return err
		}

		multipartWriter.WriteField("filePath", remoteDir)
		multipartWriter.WriteField("transfer", "1")
		multipartWriter.WriteField("block", strconv.Itoa(block))
		multipartWriter.WriteField("size", strconv.Itoa(writeSize))
		md5Hex := md5.New()
		md5Hex.Write(buf[:size])
		md5Res = hex.EncodeToString(md5Hex.Sum(nil))
		multipartWriter.WriteField("md5", md5Res)
		multipartWriter.WriteField("pos", strconv.Itoa(pos))

		bodyCloseErr := multipartWriter.Close()
		if bodyCloseErr != nil {
			return bodyCloseErr
		}
		resp, err := client.Post(url, multipartWriter.FormDataContentType(), &body)

		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			return fmt.Errorf("远程端响应码为：%d", resp.StatusCode)
		}

		// 文件已经读取结束
		if err == io.EOF {
			break
		}

		// 分块上传结束，偏移量增加
		pos += writeSize
	}
	logrus.Info("断点续传上传文件至远程端成功")
	return nil
}
